/*
 * 文件名：hall_id.cpp
 * 功能描述：霍尔传感器棋盘识别和棋子移动控制
 */
#include "hall_id.h"
#include <Arduino.h>
#include <BLE2902.h>
#include <ArduinoJson.h>
#include "bsp_ble.h"
#include "hbot_motion.h"
#include "bsp_motor.h"
#include "ElectromagnetControl.h"
#include <string>
#include <vector>
#include "middle.h"
#include "A_Star.h"
#include "buzzer.h"
#include "endstop.h"
#include "BatteryMonitor.h"
#include <esp_task_wdt.h>
#include "LVGL_Driver.h"

// 初始化棋盘状态存储数组
char newBoard[100]; // 存储棋盘的新状态
#define max_delay 100
// 初始化串口通信
HardwareSerial mySerial(1); // 使用硬件串口1进行通信
String rxBuffer = "";       // 用于存储接收的串口数据
char destArr[10][10];
bool gameStarted = false;  // 游戏是否开始的标志
char stage1_state[10][10]; // 用于保存第二次检测到的状态
int stage = 0;
bool Magnetstatus = false; // 状态机阶段：0-正常，1-等待第三次确认，2-等待第四次确认

QueueHandle_t commandQueue;
bool QW_Promotion = false;
bool QB_Promotion = false;

static float current_col = 0;
static float current_row = 0;

// ID板状态更新版本与时间戳
volatile uint32_t id_board_version = 0;
volatile uint32_t id_board_last_update_ms = 0;

// 在移动后等待ID板状态更新的辅助函数（带超时）
static bool waitForIdBoardUpdate(uint32_t prev_version, uint32_t timeout_ms)
{
    uint32_t start_ms = millis();
    while ((millis() - start_ms) < timeout_ms)
    {
        if (id_board_version != prev_version)
        {
            return true;
        }
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }
    return false;
}
static String idArrToHex(const char arr[10][10])
{
    String out = "";
    uint8_t nibble = 0;
    int bitCount = 0;
    for (int i = 0; i < 10; ++i)
    {
        for (int j = 0; j < 10; ++j)
        {
            char c = arr[i][j];
            uint8_t bit = (c == 'w') ? 1 : 0; // 'w' 表示有棋子，记为1；'+'表示无棋子，记为0
            nibble = (nibble << 1) | bit;
            bitCount++;
            if (bitCount == 4)
            {
                const char *hex = "0123456789ABCDEF";
                out += hex[nibble & 0x0F];
                nibble = 0;
                bitCount = 0;
            }
        }
    }
    // 理论上100位可被4整除，不会进入此分支；此处仅为稳健性
    if (bitCount > 0)
    {
        nibble <<= (4 - bitCount);
        const char *hex = "0123456789ABCDEF";
        out += hex[nibble & 0x0F];
    }
    return out;
}
/**
 * @brief 分包发送蓝牙数据
 * @param move 移动坐标
 * @param status 命令状态（success成功/failure失败）
 * @param err 报错原因
 */
void sendMoveResponse(String move, String status, String err)
{
    // 创建JSON文档
    StaticJsonDocument<256> response;

    // 设置JSON字段
    response["cmd_name"] = "move";
    response["move"] = move;
    response["status"] = status;
    response["err"] = err;

    // 序列化JSON
    String responseStr;
    serializeJson(response, responseStr);

    // 发送响应
    if (deviceConnected_g)
    {
        pCharacteristic->setValue(responseStr.c_str());
        pCharacteristic->notify();
    }
}
/**
 * @brief 分包发送蓝牙数据
 * @param cmd_name 命令名称
 * @param status 命令状态（success成功/failure失败）
 * @param err 报错原因
 */
void sendIdBoardInChunks(String cmd_name, String status, String err)
{
    // 构建 JSON 数据字符串
    String jsonData =
        "{\"cmd_name\":\"" + cmd_name + "\",\"status\":\"" + status + "\",\"err\":\"" + err + "\",\"board\":[";

    // 拼接 id_board 数据
    for (int i = 0; i < 10; i++)
    {
        jsonData += "["; // 开始一行数据
        for (int j = 0; j < 10; j++)
        {
            if (j > 0)
            {
                jsonData += ","; // 每个元素之间加逗号
            }
            jsonData += "\"" + idBoard[i][j] + "\""; // 添加当前元素
        }
        jsonData += "]"; // 完成一行数据
        if (i < 9)
        {
            jsonData += ","; // 行之间加逗号（最后一行不加）
        }
    }

    jsonData += "]}"; // 完成 JSON 数据

    // 输出构建好的 JSON 数据
    if (print_key)
    {
        Serial.println(jsonData);
    }

    // 获取 JSON 字符串的总长度
    int totalLength = jsonData.length();

    // 计算总批次数，每批次不超过 251 字节
    int totalBatches = (totalLength + 250) / 251; // 251 字节为最大负载

    for (int batchNum = 0; batchNum < totalBatches; batchNum++)
    {
        // 计算当前批次的起始和结束索引
        int startIdx = batchNum * 251;
        int endIdx = min((batchNum + 1) * 251, totalLength);

        // 获取当前批次的字符串部分
        String chunk = jsonData.substring(startIdx, endIdx);

        // 发送当前数据包
        if (deviceConnected_g)
        {
            pCharacteristic->setValue(chunk.c_str()); // 发送分割的字符串
            pCharacteristic->notify();                // 通知上位机
        }
        vTaskDelay(50 / portTICK_PERIOD_MS); // 增加延迟以防丢包
    }
}

/**
 * @brief 发送human_move命令的响应
 * @param status 命令状态（success成功/failure失败）
 * @param err 报错原因
 * @param human_move 走棋记录（例如"e7d5"）
 * @param capture_square 被吃棋子的位置（如果有）
 */
void sendHumanMoveResponse(String huamn_move, String capture_square, String status, String err)
{
    // 使用ArduinoJson库创建响应
    StaticJsonDocument<512> response;
    response["cmd_name"] = "human_move";
    response["human_move"] = huamn_move;
    response["capture_square"] = capture_square;
    response["status"] = status;
    response["err"] = err;

    // 序列化JSON
    String responseStr;
    serializeJson(response, responseStr);

    // 输出构建好的JSON数据
    // Serial.println(responseStr);

    // 获取JSON字符串的总长度
    int totalLength = responseStr.length();

    // 计算总批次数，每批次不超过251字节
    int totalBatches = (totalLength + 250) / 251;

    for (int batchNum = 0; batchNum < totalBatches; batchNum++)
    {
        // 计算当前批次的起始和结束索引
        int startIdx = batchNum * 251;
        int endIdx = min((batchNum + 1) * 251, totalLength);

        // 获取当前批次的字符串部分
        String chunk = responseStr.substring(startIdx, endIdx);

        // 发送当前数据包
        if (deviceConnected_g)
        {
            pCharacteristic->setValue(chunk.c_str());
            pCharacteristic->notify();
        }
        vTaskDelay(50 / portTICK_PERIOD_MS); // 增加延迟以防丢包
    }
}
void print_ID_Board(char id[10][10])
{
    // 开始构建 JSON 数据字符串
    String ID_Board_Data = "";
    // 判断是否传入了 id_board 数据（即 id 数组是否有效）
    if (id != nullptr)
    {
        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                ID_Board_Data += String((id[i][j])); // 添加当前元素
            }
        }
    }
    if (print_key)
    {
        Serial.println(ID_Board_Data);
        Serial.println();
    }
}
/**
 * @brief 串口以及蓝牙输出id板状态
 * @param id 10x10id板目前状态
 * 定时发ID板数据
 */
void send_print(char id[10][10])
{
    StaticJsonDocument<512> response;
    String responseStr;

    // 开始构建 JSON 数据字符串
    String jsonData = "";
    // 判断是否传入了 id_board 数据（即 id 数组是否有效）
    if (id != nullptr)
    {
        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                jsonData += String((id[i][j])); // 添加当前元素
            }
        }
    }

    // response["id_board"] = jsonData;
    response["id_board_hex"] = idArrToHex(id);
    serializeJson(response, responseStr);
    if (print_key)
    {
        Serial.println(responseStr);
    }

    // 获取 JSON 字符串的总长度
    int totalLength = responseStr.length();
    int totalBatches = (totalLength + 250) / 251; // 251 字节为最大负载
    for (int batchNum = 0; batchNum < totalBatches; batchNum++)
    {
        // 计算当前批次的起始和结束索引
        int startIdx = batchNum * 251;
        int endIdx = min((batchNum + 1) * 251, totalLength);
        // 获取当前批次的字符串部分
        String chunk = jsonData.substring(startIdx, endIdx);
        // 发送当前数据包
        if (deviceConnected_g)
        {
            pCharac_state_msg->setValue(responseStr.c_str()); // 发送分割的字符串
            pCharac_state_msg->notify();                      // 通知上位机
            if (print_key)
            {
                Serial.println("发送成功");
            }
        }
        vTaskDelay(50 / portTICK_PERIOD_MS); // 增加延迟以防丢包
    }
}
/**
 * @brief 发送打印信息
 * @param cmd_name 命令名称
 * @param status 命令状态（success成功/failure失败）
 * @param err 报错原因
 * @param id_board 10x10的ID矩阵
 * @param id
 * free_move sog
 */
void sendprint(String cmd_name, String status, String err, String id_board[10][10], String id)
{
    // 开始构建 JSON 数据字符串
    String jsonData = "{\"cmd_name\":\"" + cmd_name + "\",\"status\":\"" + status + "\",\"err\":\"" + err + "\"";
    if (id.length() > 0)
    { // 只有当id不为空时才添加
        jsonData += ",\"id\":\"" + id + "\"";
    }
    // 只在 id_board 参数有效时添加该字段
    bool hasIdBoard = false;

    // 判断是否传入了 id_board 数据（即 id 数组是否有效）
    if (id_board != nullptr)
    {
        jsonData += ",\"id_board\":["; // 添加 id_board 字段

        for (int i = 0; i < 10; i++)
        {
            jsonData += "["; // 开始一行数据
            for (int j = 0; j < 10; j++)
            {
                if (j > 0)
                {
                    jsonData += ","; // 每个元素之间加逗号
                }
                jsonData += "\"" + String(id_board[i][j]) + "\""; // 添加当前元素
            }
            jsonData += "]"; // 完成一行数据
            if (i < 9)
            {
                jsonData += ","; // 行之间加逗号（最后一行不加）
            }
        }
        jsonData += "]"; // 完成 id_board 数据
        hasIdBoard = true;
    }

    jsonData += "}"; // 完成 JSON 数据

    // 输出构建好的 JSON 数据
    if (print_key)
    {
        Serial.println(jsonData);
    }

    // 获取 JSON 字符串的总长度
    int totalLength = jsonData.length();

    // 计算总批次数，每批次不超过 251 字节
    int totalBatches = (totalLength + 250) / 251; // 251 字节为最大负载

    for (int batchNum = 0; batchNum < totalBatches; batchNum++)
    {
        // 计算当前批次的起始和结束索引
        int startIdx = batchNum * 251;
        int endIdx = min((batchNum + 1) * 251, totalLength);

        // 获取当前批次的字符串部分
        String chunk = jsonData.substring(startIdx, endIdx);

        // 发送当前数据包
        if (deviceConnected_g)
        {
            pCharacteristic->setValue(chunk.c_str()); // 发送分割的字符串
            pCharacteristic->notify();                // 通知上位机
        }
        vTaskDelay(50 / portTICK_PERIOD_MS); // 增加延迟以防丢包
    }
}

/**
 * @brief 发送空ID矩阵
 * @details 用于初始化错误时发送空矩阵
 */
void sendEmptyIdBoardInChunks()
{
    // 构建 JSON 数据字符串
    String jsonData = "{\"cmd_name\":\"start_new_game\",\"status\":\"failure\",\"err\":\"The chess pieces are not placed properly\",\"id_board\":[]}"; // 直接将空数组传递给 id_Board
    // 获取 JSON 字符串的总长度
    int totalLength = jsonData.length();
    // 计算总批次数，每批次不超过 20 字节
    int totalBatches = (totalLength + 19) / 20; // 加 19 确保舍入到最近的整数

    // 打印总长度和批次数
    if (print_key)
    {
        Serial.printf("Total length: %d, Total batches: %d\n", totalLength, totalBatches);
    }

    for (int batchNum = 0; batchNum < totalBatches; batchNum++)
    {
        // 计算当前批次的起始和结束索引
        int startIdx = batchNum * 20;
        int endIdx = min((batchNum + 1) * 20, totalLength);

        // 获取当前批次的字符串部分
        String chunk = jsonData.substring(startIdx, endIdx);

        if (print_key)
        {
            Serial.println(chunk); // 发送给串口监视器，帮助调试
        }

        // 发送当前数据包
        if (deviceConnected_g)
        {
            pCharacteristic->setValue(chunk.c_str()); // 发送分割的字符串
            pCharacteristic->notify();                // 通知上位机
        }
        vTaskDelay(50 / portTICK_PERIOD_MS); // 增加延迟以防丢包
    }
}

/**
 * @brief 根据位置获取电磁铁编号
 * @param endPos 终点位置
 * @return 电磁铁编号
 */
int getMagnetNumFromPosition(const String &startPos, const String &endPos)
{
    // 获取终点坐标的第一位和第二位
    char firstChar_start = startPos.charAt(0);  // 第一位
    char secondChar_start = startPos.charAt(1); // 第二位
    char firstChar_end = endPos.charAt(0);      // 第一位
    char secondChar_end = endPos.charAt(1);     // 第二位

    // 根据终点坐标的列来决定电磁铁号
    if (firstChar_end == '0')
    {
         Serial.println("选择电磁铁1--getMagnetNumFromPosition");
         /*
            20250925
            选择完电磁铁，让其余电磁铁断电
         */
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 1;
    }
    else if (firstChar_end == '9')
    {
        Serial.println("选择电磁铁3--getMagnetNumFromPosition");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 3;
    }
    else if (secondChar_end == '0')
    {
        Serial.println("选择电磁铁4--getMagnetNumFromPosition");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 4;
    }
    else if (secondChar_end == '9')
    {
         Serial.println("选择电磁铁2--getMagnetNumFromPosition");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 2;
    }

    else if (firstChar_start == '0')
    {
         Serial.println("选择电磁铁1--getMagnetNumFromPosition");
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 1;
    }
    else if (firstChar_start == '9')
    {
         Serial.println("选择电磁铁3--getMagnetNumFromPosition");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 3;
    }
    else if (secondChar_start == '0')
    {
         Serial.println("选择电磁铁4--getMagnetNumFromPosition");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 4;
    }
    else if (secondChar_start == '9')
    {
         Serial.println("选择电磁铁2--getMagnetNumFromPosition");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 2;
    }

    // 如果不符合任何条件，则返回默认电磁铁号（假设为1号电磁铁）
     Serial.println("选择电磁铁5--getMagnetNumFromPosition");
    digitalWrite(MEAG1, LOW);
    digitalWrite(MEAG4, LOW);
    digitalWrite(MEAG3, LOW);
    digitalWrite(MEAG2, LOW);
    delay(max_delay);
    return 5;
    
}
int getMagnetNumFromPosition_DS(const String &startPos, const String &endPos)
{
    // 获取终点坐标的第一位和第二位
    char firstChar_start = startPos.charAt(0);  // 第一位
    char secondChar_start = startPos.charAt(1); // 第二位
    char firstChar_end = endPos.charAt(0);      // 第一位
    char secondChar_end = endPos.charAt(1);     // 第二位

    // 根据终点坐标来决定电磁铁号
    if (firstChar_end == '0')
    {
         Serial.println("选择电磁铁1--getMagnetNumFromPosition_DS");
        /*
            20250925
            选择完电磁铁，让其余电磁铁断电
         */
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 1;
    }
    else if (firstChar_end == '9')
    {
         Serial.println("选择电磁铁3--getMagnetNumFromPosition_DS");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 3;
    }
    else if (secondChar_end == '0')
    {
         Serial.println("选择电磁铁4--getMagnetNumFromPosition_DS");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG2, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 4;
    }
    else if (secondChar_end == '9')
    {
         Serial.println("选择电磁铁2--getMagnetNumFromPosition_DS");
        digitalWrite(MEAG1, LOW);
        digitalWrite(MEAG4, LOW);
        digitalWrite(MEAG3, LOW);
        digitalWrite(MEAG5, LOW);
        delay(max_delay);
        return 2;
    }
    // 如果不符合任何条件，则返回默认电磁铁号（假设为5号电磁铁）
     Serial.println("选择电磁铁5--getMagnetNumFromPosition_DS");
    digitalWrite(MEAG1, LOW);
    digitalWrite(MEAG4, LOW);
    digitalWrite(MEAG3, LOW);
    digitalWrite(MEAG2, LOW);
    delay(max_delay);
    return 5;
}
/**
 * @brief 电磁铁移动函数（模式一）
 * @param x_move X轴移动距离
 * @param y_move Y轴移动距离
 * @param magnet_num_move 电磁铁编号
 */
void magnet_move_one(float x_move, float y_move, int magnet_num_move)
{
    MOTOR_ENABLE; // 启动电机

    setElectromagnet(magnet_num_move); // 设置电磁铁
                                       // 停止当前运动并重置目标
    esp_task_wdt_reset();
    Xaxis.stop();
    Yaxis.stop();
    Xaxis.setCurrentPosition(Xaxis.currentPosition()); // 清除未完成的目标
    Yaxis.setCurrentPosition(Yaxis.currentPosition());
    // 计算步进值
    long tmp_hbot_position[2];
    //将物理坐标转换为步进电机步数
    MotorSteps s5 = calc_vector_x(x_move);
    MotorSteps s6 = calc_vector_y(y_move);
    tmp_hbot_position[0] = s5.m1_step_value + s6.m1_step_value;
    tmp_hbot_position[1] = s5.m2_step_value + s6.m2_step_value;
    // 更新目标位置
    long hbot_position[2];
    hbot_position[0] = tmp_hbot_position[0];
    hbot_position[1] = tmp_hbot_position[1];

    // 设置目标位置
    Xaxis.moveTo(hbot_position[0]);
    Yaxis.moveTo(hbot_position[1]);

    long totalDistancex = 0; // 总行程（步数）
    long totalDistancey = 0; // 总行程（步数）

    Xaxis.setAcceleration(speed_g * 0.8); // 加速度设为0
    Yaxis.setAcceleration(speed_g * 0.8);
    totalDistancex = abs(hbot_position[0] - Xaxis.currentPosition()); // 计算总行程
    totalDistancey = abs(hbot_position[1] - Yaxis.currentPosition()); // 计算总行程

    while (Xaxis.distanceToGo() != 0 || Yaxis.distanceToGo() != 0)
    {
        // 实时计算剩余距离百分比
        float remainingPercentx = abs(Xaxis.distanceToGo()) / (float)totalDistancex * 100;
        float remainingPercenty = abs(Xaxis.distanceToGo()) / (float)totalDistancey * 100;

        // 剩余50%时触发匀速逻辑
        if (remainingPercentx <= 40)
        {
            Xaxis.setAcceleration(1e9); // 设为极大加速度（近似匀速）
        }
        if (remainingPercenty <= 40)
        {
            Yaxis.setAcceleration(1e9); // 设为极大加速度（近似匀速）
        }
        Xaxis.run();
        Yaxis.run();
    }
    controlElectromagnet(true);
    // 停止当前运动并重置目标
    Xaxis.stop();
    Yaxis.stop();
    Xaxis.setCurrentPosition(Xaxis.currentPosition()); // 清除未完成的目标
    Yaxis.setCurrentPosition(Yaxis.currentPosition());
    //    MOTOR_DISABLE;
}

/**
 * @brief 电磁铁移动函数（模式二）
 * @param x_move X轴移动距离
 * @param y_move Y轴移动距离
 * @param magnet_num_move 电磁铁编号
 */
void magnet_move_two(float x_move, float y_move, int magnet_num_move)
{
    MOTOR_ENABLE;                      // 启动电机
    setElectromagnet(magnet_num_move); // 设置电磁铁
                                       // 停止当前运动并重置目标
    Xaxis.stop();
    Yaxis.stop();
    Xaxis.setCurrentPosition(Xaxis.currentPosition()); // 清除未完成的目标
    Yaxis.setCurrentPosition(Yaxis.currentPosition());
    // 计算步进值
    long tmp_hbot_position[2];
    MotorSteps s5 = calc_vector_x(x_move);
    MotorSteps s6 = calc_vector_y(y_move);
    tmp_hbot_position[0] = s5.m1_step_value + s6.m1_step_value;
    tmp_hbot_position[1] = s5.m2_step_value + s6.m2_step_value;
    // 更新目标位置
    long hbot_position[2];
    hbot_position[0] = tmp_hbot_position[0];
    hbot_position[1] = tmp_hbot_position[1];

    // 设置目标位置
    Xaxis.moveTo(hbot_position[0]);

    Yaxis.moveTo(hbot_position[1]);

    Xaxis.setAcceleration(speed_g * 10); // 加速度设为0
    Yaxis.setAcceleration(speed_g * 10);

    while (Xaxis.distanceToGo() != 0 || Yaxis.distanceToGo() != 0)
    {
        Xaxis.run();
        Yaxis.run();
    }
    controlElectromagnet(true);
    // 停止当前运动并重置目标
    Xaxis.stop();
    Yaxis.stop();
    Xaxis.setCurrentPosition(Xaxis.currentPosition()); // 清除未完成的目标
    Yaxis.setCurrentPosition(Yaxis.currentPosition());
    //    MOTOR_DISABLE;
}

/**
 * @brief 电磁铁移动函数
 * @param x_move X轴移动距离
 * @param y_move Y轴移动距离
 * @param magnet_num_move 电磁铁编号
 */
float last_x_move = 0, last_y_move = 0;

void magnet_move(float x_move, float y_move, int magnet_num_move)
{
    MOTOR_ENABLE;
    setElectromagnet(magnet_num_move);
    esp_task_wdt_reset(); // 初始化时重置看门狗

    // 停止当前运动并重置目标
    Xaxis.stop();
    Yaxis.stop();
    Xaxis.setCurrentPosition(Xaxis.currentPosition());
    Yaxis.setCurrentPosition(Yaxis.currentPosition());

    //--- 判断是否为斜向运动 ---
    bool is_diagonal = false;
    float tolerance = 0.001; // 容差阈值，防止浮点误差误判
   //判断坐标的变化，判断是否是斜向运动
    float dx_move = x_move - last_x_move;
    float dy_move = y_move - last_y_move;
    last_x_move = x_move;
    last_y_move = y_move;
//判断是否是斜向运动
    if (fabs(dx_move) > tolerance && fabs(dy_move) > tolerance)
    {
        is_diagonal = true;
    }

    if (is_diagonal)
    {
        // 斜向运动：单轴速度提升至 speed_g * √2
        float v_axis_compensated = speed_g * 1.6;
        Xaxis.setMaxSpeed(v_axis_compensated);
        Yaxis.setMaxSpeed(v_axis_compensated);
    }
    else
    {
        // 轴向运动：单轴速度保持 speed_g
        Xaxis.setMaxSpeed(speed_g * 1.13);
        Yaxis.setMaxSpeed(speed_g * 1.13);
    }

    // 根据坐标计算步进值（原逻辑不变）
    long tmp_hbot_position[2];
    MotorSteps s5 = calc_vector_x(x_move);
    MotorSteps s6 = calc_vector_y(y_move);
    //合并两轴步数值
    tmp_hbot_position[0] = s5.m1_step_value + s6.m1_step_value;
    tmp_hbot_position[1] = s5.m2_step_value + s6.m2_step_value;

    // 设置目标位置
    Xaxis.moveTo(tmp_hbot_position[0]);
    Yaxis.moveTo(tmp_hbot_position[1]);

    // 设置加速度参数，实现加速-匀速-减速模式
    float accel_factor = 1.5; // 加速度因子，可调整
    Xaxis.setAcceleration(speed_g * accel_factor);
    Yaxis.setAcceleration(speed_g * accel_factor);

    // 实现加速-匀速-减速运动
    while (Xaxis.distanceToGo() != 0 || Yaxis.distanceToGo() != 0)
    {
        // 周期性重置看门狗计时器
        static unsigned long lastWdtReset = 0;
        unsigned long currentMillis = millis();
        if (currentMillis - lastWdtReset > 50)
        { // 每50毫秒重置一次
            esp_task_wdt_reset();
            lastWdtReset = currentMillis;
        }

        Xaxis.run();
        Yaxis.run();
    }
    //移动调整结束，开启电磁铁
    controlElectromagnet(true);
    esp_task_wdt_reset(); // 结束时重置看门狗
    MOTOR_DISABLE;
}
/////////////////////////////////////////////////////////////////////////////
/*
20250925
修改电机移动函数，增加反馈机制
*/
// 全局定义步进电机参数（根据您的实际配置调整）
// const float STEPS_PER_REV = 200.0;   // 42步进电机通常为200步/转（1.8度步距角）
// const float MICROSTEPS = 16.0;       // 驱动器微步设置（常见为16细分）
// const float LEAD_SCREW_PITCH = 2.0;  // 丝杠导程（单位：mm）
// const float STEPS_PER_MM = (STEPS_PER_REV * MICROSTEPS) / LEAD_SCREW_PITCH;
// float last_x_move = 0, last_y_move = 0;
// // 将X轴步数转换为物理坐标
// float inverse_calc_vector_x(long steps)
// {
//     return steps / STEPS_PER_MM;
// }

// // 将Y轴步数转换为物理坐标
// float inverse_calc_vector_y(long steps)
// {
//     return steps / STEPS_PER_MM;
// }

// void magnet_move(float x_move, float y_move, int magnet_num_move)
// {
//     MOTOR_ENABLE;
//     setElectromagnet(magnet_num_move);
//     esp_task_wdt_reset(); // 初始化时重置看门狗

//     // 停止当前运动并重置目标
//     Xaxis.stop();
//     Yaxis.stop();
//     Xaxis.setCurrentPosition(Xaxis.currentPosition());
//     Yaxis.setCurrentPosition(Yaxis.currentPosition());

//     //--- 判断是否为斜向运动 ---
//     bool is_diagonal = false;
//     float tolerance = 0.001; // 容差阈值，防止浮点误差误判

//     float dx_move = x_move - last_x_move;
//     float dy_move = y_move - last_y_move;
//     last_x_move = x_move;
//     last_y_move = y_move;

//     if (fabs(dx_move) > tolerance && fabs(dy_move) > tolerance)
//     {
//         is_diagonal = true;
//     }

//     if (is_diagonal)
//     {
//         // 斜向运动：单轴速度提升至 speed_g * √2
//         float v_axis_compensated = speed_g * 1.6;
//         Xaxis.setMaxSpeed(v_axis_compensated);
//         Yaxis.setMaxSpeed(v_axis_compensated);
//     }
//     else
//     {
//         // 轴向运动：单轴速度保持 speed_g
//         Xaxis.setMaxSpeed(speed_g * 1.13);
//         Yaxis.setMaxSpeed(speed_g * 1.13);
//     }

//     // 计算步进值（原逻辑不变）
//     long tmp_hbot_position[2];
//     MotorSteps s5 = calc_vector_x(x_move);
//     MotorSteps s6 = calc_vector_y(y_move);
//     tmp_hbot_position[0] = s5.m1_step_value + s6.m1_step_value;
//     tmp_hbot_position[1] = s5.m2_step_value + s6.m2_step_value;

//     // 设置目标位置
//     Xaxis.moveTo(tmp_hbot_position[0]);
//     Yaxis.moveTo(tmp_hbot_position[1]);

//     // 设置加速度参数，实现加速-匀速-减速模式
//     float accel_factor = 1.5; // 加速度因子，可调整
//     Xaxis.setAcceleration(speed_g * accel_factor);
//     Yaxis.setAcceleration(speed_g * accel_factor);

//     // 实现加速-匀速-减速运动
//     while (Xaxis.distanceToGo() != 0 || Yaxis.distanceToGo() != 0)
//     {
//         // 周期性重置看门狗计时器
//         static unsigned long lastWdtReset = 0;
//         unsigned long currentMillis = millis();
//         if (currentMillis - lastWdtReset > 50)
//         { // 每50毫秒重置一次
//             esp_task_wdt_reset();
//             lastWdtReset = currentMillis;
//         }

//         Xaxis.run();
//         Yaxis.run();
//     }
    
//     // 移动结束后，验证实际位置是否达到目标位置
//     float positionTolerance = 0.1; // 位置容差，单位mm（42步进电机通常精度较高）
    
//     // 获取实际步数位置
//     long actualStepsX = Xaxis.currentPosition();
//     long actualStepsY = Yaxis.currentPosition();
    
//     // 将步数转换为物理坐标
//     float actualX = inverse_calc_vector_x(actualStepsX);
//     float actualY = inverse_calc_vector_y(actualStepsY);
    
//     // 计算位置误差
//     float errorX = actualX - x_move;
//     float errorY = actualY - y_move;
//     float totalError = sqrt(errorX * errorX + errorY * errorY);
    
//     // 打印位置验证结果
//     if (print_key)
//     {
//         Serial.printf("目标位置: (%.2f, %.2f) mm\n", x_move, y_move);
//         Serial.printf("实际位置: (%.2f, %.2f) mm\n", actualX, actualY);
//         Serial.printf("位置误差: X=%.4fmm, Y=%.4fmm, 总误差=%.4fmm\n", errorX, errorY, totalError);
        
//         if (fabs(errorX) <= positionTolerance && fabs(errorY) <= positionTolerance)
//         {
//             Serial.println("位置验证: 成功 (误差在容差范围内)");
//         }
//         else
//         {
//             Serial.println("位置验证: 失败 (误差超出容差范围)");
//             Serial.println("可能原因: 机械回差、电机丢步或负载过大");
//         }
//     }
    
//     // 如果误差过大，尝试修正位置（可选）
//     if (fabs(errorX) > positionTolerance || fabs(errorY) > positionTolerance)
//     {
//         // 尝试微调位置
//         if (print_key)
//         {
//             Serial.println("尝试位置修正...");
//         }
        
//         // 重新设置目标位置（使用实际步数作为起点）
//         Xaxis.setCurrentPosition(actualStepsX);
//         Yaxis.setCurrentPosition(actualStepsY);
//         Xaxis.moveTo(tmp_hbot_position[0]);
//         Yaxis.moveTo(tmp_hbot_position[1]);
        
//         // 低速移动以减小误差
//         float originalSpeed = speed_g;
//         speed_g = originalSpeed * 0.5; // 降低速度以提高精度
        
//         while (Xaxis.distanceToGo() != 0 || Yaxis.distanceToGo() != 0)
//         {
//             Xaxis.run();
//             Yaxis.run();
//         }
        
//         speed_g = originalSpeed; // 恢复原始速度
        
//         // 再次检查位置
//         actualStepsX = Xaxis.currentPosition();
//         actualStepsY = Yaxis.currentPosition();
//         actualX = inverse_calc_vector_x(actualStepsX);
//         actualY = inverse_calc_vector_y(actualStepsY);
        
//         if (print_key)
//         {
//             Serial.printf("修正后位置: (%.2f, %.2f) mm\n", actualX, actualY);
//         }
//     }
    
//     //移动调整结束，开启电磁铁
//     controlElectromagnet(true);
//     esp_task_wdt_reset(); // 结束时重置看门狗
//     MOTOR_DISABLE;
// }

/**
 * @brief 接收ID板矩阵数据
 */
void receiveMatrix()
{
    char receiveBoard[101];
    rxBuffer = "";
    bool startReceiving = false;

    while (true)
    {
        if (mySerial.available())
        {
            char c = mySerial.read();

            if (c == '1' && !startReceiving)
            {
                startReceiving = true;
                continue;
            }

            if (c == '0' && startReceiving)
            {
                rxBuffer += c;
                break;
            }

            if (startReceiving)
            {
                rxBuffer += c;
            }
        }
    }

    rxBuffer.toCharArray(receiveBoard, sizeof(receiveBoard));
    for (int i = 0; i < 100; i++)
    {
        newBoard[i] = receiveBoard[i];
    }
    delay(1);
}

/**
 * @brief 扫描任务
 * @details 调试+接收ID板串口数据
 */
void scannerTask(void *pvParameters)
{
    while (true)
    {
        if (Magnetstatus == false)
        {
            receiveMatrix(); // 调用函数接收矩阵数据
            transformBoard();

            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    if (finalArr[i][j] != 'w' && finalArr[i][j] != '+')
                    {
                        finalArr[i][j] = '+'; // 强制替换为'+'
                    }
                }
            }
            // 状态机处理逻辑
            if (stage == 0)
            {
                // 正常阶段：比较当前状态和上一次稳定状态
                if (memcmp(destArr, finalArr, sizeof(destArr)) != 0)
                {
                    // 发现不同，进入确认阶段1（保存当前状态）
                    memcpy(stage1_state, finalArr, 10 * 10 * sizeof(char));
                    stage = 1;
                }
            }
            else if (stage == 1)
            {
                // 确认阶段1：检查第三次状态是否与第二次一致
                if (memcmp(stage1_state, finalArr, sizeof(stage1_state)) == 0)
                {
                    stage = 2; // 进入确认阶段2
                }
                else
                {
                    stage = 0; // 不一致，重置状态机
                }
            }
            else if (stage == 2)
            {
                // 确认阶段2：检查第四次状态是否与第二次一致
                if (memcmp(stage1_state, finalArr, sizeof(stage1_state)) == 0)
                {

                    // 满足所有条件，发送并更新稳定状态
                    send_print(stage1_state);
                    memcpy(destArr, stage1_state, 10 * 10 * sizeof(char));
                    id_board_version++;
                    id_board_last_update_ms = millis();
                }
                stage = 0; // 无论是否匹配都重置状态机
            }
        }
        batteryLevel_g = readBatteryStatus();
    }
}

/**
 * @brief 检查白棋是否缺失
 * @return 缺失的位置信息
 */
String checkW()
{
    String missingPositions = ""; // Store positions that do not have 'w'

    // 检查 (1, 1) 到 (1, 8)
    for (int i = 1; i <= 8; i++)
    {
        if (finalArr[1][i] != 'w')
        {

            missingPositions += grid[1][i] + ",";
        }
    }

    // 检查 (2, 1) 到 (2, 8)
    for (int i = 1; i <= 8; i++)
    {
        if (finalArr[2][i] != 'w')
        {
            missingPositions += grid[2][i] + ",";
        }
    }

    // 检查 (7, 1) 到 (7, 8)
    for (int i = 1; i <= 8; i++)
    {
        if (finalArr[7][i] != 'w')
        {
            missingPositions += grid[7][i] + ",";
        }
    }

    // 检查 (8, 1) 到 (8, 8)
    for (int i = 1; i <= 8; i++)
    {
        if (finalArr[8][i] != 'w')
        {
            missingPositions += grid[8][i] + ",";
        }
    }
    return missingPositions;
}

// 打印 19x19 的字符网格
void printGrid(char grid[GRID_SIZE][GRID_SIZE])
{
    for (int i = 0; i < GRID_SIZE; i++)
    {
        for (int j = 0; j < GRID_SIZE; j++)
        {
            Serial.print(grid[i][j]);
            Serial.print(" "); // 打印字符并加上空格
        }
        +Serial.println(); // 换行
    }
}

/**
 * @brief 沿着路径移动电磁铁
 * @param path_19x19 19x19的路径
 * @param magnetNum 电磁铁编号
 */

void moveMagnetAlongPath(std::vector<std::pair<int, int>> &path_19x19, int magnetNum)
{
    if (path_19x19.empty()) // 如果路径为空，则直接返回
        return;

    // 打印所有路径点
    if (print_key)
    {
        Serial.println("所有路径坐标点如下:，坐标是列在前，行在后");
        for (size_t i = 0; i < path_19x19.size(); ++i)
        {
            Serial.printf("点 %d: (%d, %d)\n", i, path_19x19[i].first, path_19x19[i].second);
        }
    }

    // 定义移动类型枚举
    enum ChessMoveType
    {
        CHESS_HORIZONTAL, // 横向移动
        CHESS_VERTICAL,   // 竖向移动
        CHESS_DIAGONAL,   // 斜向移动
        CHESS_UNKNOWN

    };

    // 返回两点间移动类型的辅助函数
    //判断以什么形式走
    auto getMoveType = [](const std::pair<int, int> &p1, const std::pair<int, int> &p2) -> ChessMoveType
    {
        if (p1.first == p2.first) // 行相同，为横向移动
        {
            return CHESS_HORIZONTAL;
        }
        else if (p1.second == p2.second) // 列相同，为竖向移动
        {
            return CHESS_VERTICAL;
        }
        else // 既不是同行也不是同列，为斜向移动
        {
            return CHESS_DIAGONAL;
        }
    };

    // 计算物理坐标的函数
    auto calculateCoords = [magnetNum](const std::pair<int, int> &point, float &x, float &y)
    {
        switch (magnetNum)
        {
        case 1:
            y = (((float)point.first * 25) - 50);
            x = (((float)point.second * 25) - 50) + 50;
            Magnetstatus = true;
            break;
        case 2:
            y = (((float)point.first * 25) - 50) - 50;
            x = (((float)point.second * 25) - 50);
            Magnetstatus = true;
            break;
        case 3:
            y = (((float)point.first * 25) - 50);
            x = (((float)point.second * 25) - 50) - 50;
            Magnetstatus = true;
            break;
        case 4:
            y = (((float)point.first * 25) - 50) + 50;
            x = (((float)point.second * 25) - 50);
            Magnetstatus = true;
            break;
        case 5:
            y = (((float)point.first * 25) - 50);
            x = (((float)point.second * 25) - 50);
            Magnetstatus = true;
            break;
        default:
            if (print_key)
            {
                Serial.println("Invalid magnet number!");
            }
            x = 0;
            y = 0;
            break;
        }
    };

    // 处理第一个点
    //将电磁铁移动到起点
    const auto &startPoint = path_19x19[0];
    float startX, startY;
    //根据电磁铁编号去改变路径，之前的路径默认是中间电磁铁的路径,根据返回的x，y进行偏移
    calculateCoords(startPoint, startX, startY);

    // 移动到起始位置
    if (print_key)
    {
        Serial.printf("Starting move to (%d,%d)\n", startPoint.second, startPoint.first);
    }
    //移动到第一个点
    magnet_move(startX, startY, magnetNum);

    // 移动后重置看门狗
    esp_task_wdt_reset();

    // 起始位置停留
    vTaskDelay(200 / portTICK_PERIOD_MS);

    if (path_19x19.size() > 1)
    {
        ChessMoveType currentType = CHESS_UNKNOWN;
        ChessMoveType prevType = CHESS_UNKNOWN;
        std::pair<int, int> targetPoint = startPoint; // 当前目标点
        float targetX, targetY;

        for (size_t i = 1; i < path_19x19.size(); ++i)
        {
            // 每4个点周期性重置看门狗，避免长路径计算超时
            if (i % 4 == 0)
            {
                esp_task_wdt_reset();
            }

            const auto &point = path_19x19[i];
            currentType = getMoveType(path_19x19[i - 1], point);

            // 第一段移动或移动类型改变时
            if (i == 1 || currentType != prevType)
            {
                // 记录新的目标点
                targetPoint = point;
                calculateCoords(targetPoint, targetX, targetY);
                prevType = currentType;
            }
            // 连续相同类型移动，更新目标点
            else if (currentType == prevType)
            {
                // Serial.printf("合并移动 - 当前点(%d,%d)与前一点类型相同(%s)\n",
                //               point.first, point.second,
                //               currentType == CHESS_HORIZONTAL ? "horizontal" : (currentType == CHESS_VERTICAL ? "vertical" : "diagonal"));
                targetPoint = point;
                calculateCoords(targetPoint, targetX, targetY);
            }

            // 如果是最后一个点或下一个点会导致移动类型改变，则执行移动
            if (i == path_19x19.size() - 1 ||
                (i + 1 < path_19x19.size() && getMoveType(point, path_19x19[i + 1]) != currentType))
            {
                // 打印合并后的移动信息
                //  Serial.printf("Moving to (%d,%d) - %s (物理坐标: x=%.2f, y=%.2f)\n",
                //                targetPoint.first, targetPoint.second,
                //                currentType == CHESS_HORIZONTAL ? "horizontal" : (currentType == CHESS_VERTICAL ? "vertical" : "diagonal"),
                //                targetX, targetY);



                //电磁铁路径的组成
                magnet_move(targetX, targetY, magnetNum);

                // 移动后重置看门狗
                esp_task_wdt_reset();

                prevType = CHESS_UNKNOWN; // 重置为下一段移动
            }
        }
    }

    // 关闭电磁铁
    controlElectromagnet(false);
    /*
        20250925 
        确定关闭
    */
    digitalWrite(MEAG1, LOW);
    digitalWrite(MEAG2, LOW);
    digitalWrite(MEAG3, LOW);
    digitalWrite(MEAG4, LOW);
    digitalWrite(MEAG5, LOW);
    Magnetstatus = false;
    /*
        20250925 
        增加移动完成后关闭电磁铁的延时
    */
    delay(500);
    esp_task_wdt_reset(); // 结束时重置看门狗
    if (print_key)
    {
        Serial.println("移动完成---moveMagnetAlongPath");
    }
}

/**
 * @brief 自动执行棋子移动全流程
 * @param piece 棋子类型标识字符串
 * @param startPos 起始位置编码（如"A1"）
 * @param endPos 目标位置编码（如"H8"）
 * @param magnetNum 磁铁编号（0表示自动推导）
 * @note 功能包含：
 * - 坐标转换（20x20网格 → 19x19路径网格）
 * - 特殊位置处理（QW/QB终点位置覆盖）
 * - A*路径规划
 * - 磁铁移动控制
 * @warning 需确保transformBoard()已正确初始化棋盘状态
 */
void auto_chess(String piece, String startPos, String endPos, int magnetNum)
{
    ////获取20*20起点和终点数组坐标，之前是10*10的
    GridPosition coords_start = getGridCoordinates(startPos);
    GridPosition coords_end = getGridCoordinates(endPos);
    //中断看门狗负责确保 ISR（中断服务程序）不被长时间阻塞，任务看门狗TWDT 负责检测任务长时间运行而不让步的情况
    esp_task_wdt_reset();
    // 路径规划********************************************************************************************************************
    // 转换棋盘
    transformBoard();
    // 初始化 19x19 网格
    char grid_19x19[19][19];
    // 初始化 19x19 网格，将 finalArr 的数据填充到 grid_19x19 中
    //将10*10变为19*19
    initializeGrid(grid_19x19, finalArr);

    // 使用字符网格找到路径
    //让编译器在编译时根据初始化表达式自动推断变量的类型，而无需程序员显式写出类型。​
    Serial.printf("Start Astar---------------------------------- ");
    auto path_19x19 = aStar(grid_19x19, std::make_pair(coords_start.row, coords_start.col),
                            std::make_pair(coords_end.row, coords_end.col));
    Serial.printf("End Astar--------------------------------------");
    /////////路径规划 END********************************************************************************************************************
    // 删除起始位置的棋子
    //deletePiece(startPos);
    if (print_key)
    {
        Serial.print("选择电磁铁-----auto_chess:");
        Serial.println(magnetNum);
    }
    // 电磁铁移动
    moveMagnetAlongPath(path_19x19, magnetNum); 
    vTaskDelay(500 / portTICK_PERIOD_MS);
    savelocation(coords_end.col, coords_end.row); // 保存机芯位置用于复位
    current_col = coords_end.col / 2;
    current_row = coords_end.row / 2;
    if (print_key)
    {
        Serial.printf("机芯位置：x:%d, y:%d\n", coords_end.col, coords_end.row);
    }
}

void path_auto_chess(std::vector<std::pair<int, int>> coordinates, int magnetNum)
{
    if (print_key)
    {
        Serial.print("选择电磁铁");
        Serial.println(magnetNum);
    }
    moveMagnetAlongPath(coordinates, magnetNum); // 移动
    vTaskDelay(500 / portTICK_PERIOD_MS);

    // savelocation(coords_end.col, coords_end.row); // 保存机芯位置用于复位
    // current_col = coords_end.col / 2;
    // current_row = coords_end.row / 2;
    // Serial.printf("机芯位置：x:%d, y:%d\n", coords_end.col, coords_end.row);
}

/**
 * @brief 悔棋
 * @param number 悔棋步数
 */
void take_back(int number)
{
    if (print_key)
    {
        Serial.println("悔棋");
    }
    for (int i = 0; i < number; i++)
    {
        ChessMove lastMove = getMoveHistory(i); // 获取最新棋步
        if (lastMove.piece != "")
        {
            int magnetNum = getMagnetNumFromPosition(lastMove.startPos, lastMove.endPos);
            if (print_key)
            {
                Serial.printf("悔棋: 棋子 %s 从 %s 移回 %s\n",
                              lastMove.piece.c_str(),
                              lastMove.endPos.c_str(),
                              lastMove.startPos.c_str());
            }
            auto_chess(lastMove.piece, lastMove.endPos, lastMove.startPos, magnetNum);
            // GridPosition coords_start = getGridCoordinates(lastMove.endPos);   // 注意这里是从终点
            // GridPosition coords_end = getGridCoordinates(lastMove.startPos);   // 到起点的移动
        }
    }
    // 清除悔棋记录
    clearMoveHistory(number);

    if (deviceConnected_g)
    {
        StaticJsonDocument<512> response;
        response["cmd_name"] = "undo_move";
        response["number"] = number;
        response["status"] = "success";
        response["err"] = "";

        String responseStr;
        serializeJson(response, responseStr);

        pCharacteristic->setValue(responseStr.c_str());
        pCharacteristic->notify();
    }
}

/**
 * @brief 蓝牙命令处理任务
 * @note 功能特性：
 * - 基于命令队列的消息处理架构
 * - 支持多种蓝牙指令类型（启动游戏/速度设置/语音控制等）
 * - 集成看门狗定时器维护系统稳定性
 */
/// 处理蓝牙命令 ----------------------------------------------------------------------------------------
void commandProcessorTask(void *pvParameters)
{

    Command cmd;
    StaticJsonDocument<256> response;
    while (true)
    {
        if (print_key)
        {
            Serial.print("BLE任务栈剩余空间：");

            Serial.println(uxTaskGetStackHighWaterMark(NULL)); // 打印当前任务的剩余栈空间
        }

        // 等待并接收命令,从队列里面拿去命令，存入cmd里面
        if (xQueueReceive(commandQueue, &cmd, portMAX_DELAY) == pdPASS)
        {
            // Serial.printf("处理命令1: %s\n", cmd.cmd_name.c_str());
            // 创建命令名称的本地副本，确保字符串完整性
            // cmdName = String(cmd.cmd_name);
            String cmdId = String(cmd.id);
            if (print_key)
            {
                Serial.printf("处理命令: %s\n", cmdName.c_str());
            }
            //            printPieceJourneys();
            // 处理 "start_new_game" 命令，开始新游戏
            if (cmdName == "start_new_game")
            {
                String checks = checkW();
                if (checks == "")
                {
                    if (print_key)
                    {
                        Serial.printf("分派ID\n");
                    }
                    gameStarted = true;
                    sendprint("start_new_game", "success", "", id_w, "0"); // 返回错误信息
                    send_print(destArr);
                }
                else
                {
                    sendprint("start_new_game", "failure", "{\"E01\":\"" + checks + "\"}", nullptr, "0"); // 返回错误信息
                }
            }
            if (cmdName == "start_game")
            {
                if (print_key)
                {
                    Serial.printf("start_game\n");
                }
                StaticJsonDocument<512> response;
                response["cmd_name"] = "start_game";
                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }
                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);

                // 如果设备已连接，则发送通知
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
                // 让语音模块输出
                sendFrameToVoiceModule(0x06);
            }
            else if (cmdName == "set_speed")
            {
                speed_set(cmd.speed);
                if (print_key)
                {
                    Serial.printf("通过蓝牙设置速度为：%d\n", cmd.speed);
                }

                StaticJsonDocument<512> response;
                response["cmd_name"] = "set_speed";
                response["status"] = "success";
                response["speed"] = cmd.speed;
                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }
                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);

                // 如果设备已连接，则发送通知
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
                // 让语音模块输出
                // sendFrameToVoiceModule(0x01);
            }
            else if (cmdName == "set_beep_volume")
            {
                beep_val = cmd.input_beep_volume;

                if (beep_val == 0)
                {
                    beep_set(0);
                }
                else if (beep_val == 1)
                {
                    beep_set(5);
                }
                else if (beep_val == 2)
                {
                    beep_set(10);
                }
                else if (beep_val == 3)
                {
                    beep_set(15);
                }
                else if (beep_val == 4)
                {
                    beep_set(20);
                }
                if (print_key)
                {
                    Serial.printf("通过蓝牙设置蜂鸣器音量为：%d\n", beep_val);
                }
                // 创建JSON响应
                StaticJsonDocument<256> response;
                response["cmd_name"] = "set_beep_volume";
                response["input_beep_volume"] = cmd.input_beep_volume;
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }
                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
                playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器

                // 让语音模块输出
                sendFrameToVoiceModule(0x02);
            }
            else if (cmdName == "set_voice_control")
            {
                if (cmd.isOpen == 0 || cmd.isOpen == 1)
                {
                    // 使用 screen.cpp 中的变量
                    voice_val = cmd.isOpen;
                    // 根据 isOpen 转换为语音控制需要的格式
                    String state = (cmd.isOpen == 1) ? "on" : "off";
                    speaker_set(state);
                    // 更新UI
                    // update_voice_state(voice_val);
                    if (print_key)
                    {
                        Serial.printf("通过蓝牙设置语音模块状态为：%d\n", cmd.isOpen);
                    }

                    StaticJsonDocument<512> response;
                    response["cmd_name"] = "set_voice_control";
                    response["status"] = "success";
                    response["isOpen"] = cmd.isOpen;
                    String responseStr;
                    serializeJson(response, responseStr);
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
            }

            else if (cmdName == "move_debug")
            {
                auto_chess("", cmd.startPos, cmd.endPos, 5);
            }
            else if (cmdName == "special_start")
            {
                // 构建响应JSON
                StaticJsonDocument<256> response;
                response["cmd_name"] = "special_start";
                response["cmd_args"] = "";

                // 序列化并发送
                String responseStr;
                serializeJson(response, responseStr);

                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println("特殊启动指令已处理");
                }
            }
            else if (cmdName == "level")
            {
                // 构建响应JSON
                StaticJsonDocument<256> response;
                response["cmd_name"] = "vocal_level_up";
                response["cmd_args"] = cmd.level;

                // 序列化并发送
                String responseStr;
                serializeJson(response, responseStr);

                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println("");
                }
            }
            else if (cmdName == "undo_move")
            {
                // 悔棋
                take_back(cmd.number);
            }
            else if (cmdName == "auto_init")
            {
                // 自动摆棋
                printPieceJourneys();
                int totalMoves = moveCount;
                // Serial.printf("当前队列中共有 %d 条棋步记录\n", totalMoves);
                // 按照索引逐个处理每个记录
                for (int i = 0; i < totalMoves; i++)
                {
                    // 获取当前索引的棋步记录
                    ChessMove currentMove = getMoveHistory(i);

                    // if (currentMove.piece == "")
                    //     continue; // 跳过空记录

                    // 调试输出
                    Serial.printf("处理记录 %d/%d: 棋子 %s 从 %s 到 %s 使用电磁铁 #%d\n",
                                  i + 1, totalMoves,
                                  currentMove.piece.c_str(),
                                  currentMove.startPos.c_str(),
                                  currentMove.endPos.c_str(),
                                  currentMove.magnetNum);

                    // 根据起始位置和终点位置的特征决定移动方式
                    if (currentMove.endPos.charAt(0) == '0' && currentMove.startPos.charAt(0) == 'h')
                    {
                        String w = lookupIdInGridRange();
                        Serial.println("0-h换磁铁");
                        auto_chess(currentMove.piece, currentMove.endPos, w, currentMove.magnetNum); // 把终点位置的旗子移到中间位置，使用的磁铁为根据中间位置选择磁铁
                        auto_chess(currentMove.piece, w, currentMove.startPos, 5);                   // 把中间位置的旗子移到起始位置，使用的磁铁为5
                    }
                    else if (currentMove.endPos.charAt(0) == '9' && currentMove.startPos.charAt(0) == 'a')
                    {
                        auto_chess(currentMove.piece, currentMove.endPos, lookupIdInGridRange(), currentMove.magnetNum);
                        auto_chess(currentMove.piece, lookupIdInGridRange(), currentMove.startPos, currentMove.magnetNum);
                    }
                    else if (checkIfEmpty(currentMove.startPos))
                    {
                        auto_chess(currentMove.piece, currentMove.endPos, currentMove.startPos, currentMove.magnetNum);
                    }
                    else if (currentMove.startPos == "QW" || currentMove.startPos == "QB")
                    {
                        auto_chess(currentMove.piece, currentMove.endPos, currentMove.startPos, currentMove.magnetNum);
                    }
                }
                clearMoveHistory(moveCount);
                resetBoard();
                gameStarted = false;
                id_board = "";
                if (deviceConnected_g)
                {
                    StaticJsonDocument<512> response;
                    response["cmd_name"] = "auto_init";
                    response["status"] = "success";
                    response["err"] = "";

                    String responseStr;
                    serializeJson(response, responseStr);

                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
            }

            else if (cmdName == "free_move")
            {

                if (print_key)
                {
                    Serial.printf("开始处理free_move命令\n");
                }
                // 检查move字段是否存在
                if (cmd.move.length() == 0)
                {
                    Serial.println("移动命令为空!");

                    if (cmd.id.length() > 0)
                    {
                        sendprint("free_move", "failure", "移动命令为空", nullptr, cmd.id);
                    }
                    else
                    {
                        sendprint("free_move", "failure", "移动命令为空", nullptr, "noid");
                    }
                    continue;
                }

                if (print_key)
                {
                    Serial.printf("free_move: %s\n", cmd.move.c_str());
                }

                if (cmd.move.length() != 4)
                {

                    if (cmd.id.length() > 0)
                    {
                        sendprint("free_move", "failure", "无效的 move 格式！", nullptr, cmd.id); // 返回错误信息
                    }
                    else
                    {
                        sendprint("free_move", "failure", "无效的 move 格式！", nullptr, "noid"); // 返回错误信息
                    }
                    continue;
                }

                // 获取起始和结束位置
                //app发送起始位置和终点位置
                String startPos = cmd.move.substring(0, 2);
                String endPos = cmd.move.substring(2, 4);
                // 转换坐标参数为判断ID板变化而服务
                int startPos_alt = processString_alt(startPos);
                int endPos_alt = processString_alt(endPos);
                // 备份移动前ID板数据
                char destArr_before[10][10];
                for (int i = 0; i < 10; ++i)
                {
                    for (int j = 0; j < 10; ++j)
                    {
                        destArr_before[i][j] = destArr[i][j];
                    }
                }
                // 记录当前版本号
                uint32_t prev_ver = id_board_version;

                // 打印坐标信息
                if (print_key)
                {
                    Serial.printf("自由移动: 从 %s 到 %s\n", startPos.c_str(), endPos.c_str());
                }
                if (startPos.charAt(0) == 'h' && endPos.charAt(0) == '0')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从h列到0列换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 5);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }

                else if (startPos.charAt(0) == 'a' && endPos.charAt(0) == '9')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从a列到9列换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 5);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(1) == '1' && endPos.charAt(1) == '9')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从1行到9行换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 5);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(1) == '8' && endPos.charAt(1) == '0')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从8行到0行换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 5);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(0) == '0' && endPos.charAt(1) == '1')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从0列行到1行换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 1);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(0) == '0' && endPos.charAt(1) == '8')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从0列到8行换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 1);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(1) == '9' && endPos.charAt(1) == '1')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从9行到1行换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 2);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(1) == '0' && endPos.charAt(1) == '8')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从0行到8行换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 4);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(1) == '9' && endPos.charAt(1) == '1')
                {
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    Serial.println("从9行到1行换磁铁");
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 4);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(0) == '0' && endPos.charAt(0) == 'h')
                {
                    Serial.println("从0列到h列换磁铁");
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    String transfer_point = lookupIdInGridRange();
                    auto_chess("", startPos, transfer_point, 1);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                else if (startPos.charAt(0) == '9' && endPos.charAt(0) == 'a')
                {
                    Serial.println("从9列到a列换磁铁");
                    int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                    String transfer_point = lookupIdInGridRange();
                    //""不用管
                    auto_chess("", startPos, transfer_point, 3);
                    auto_chess("", transfer_point, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                    playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                }
                //不需要换磁铁
                else
                {
                    // 获取使用的电磁铁号码
                    int magnetNum = getMagnetNumFromPosition(startPos, endPos);
                    // 执行移动，执行移动代码，起点，终点，电磁铁编号
                    auto_chess("", startPos, endPos, magnetNum);
                    playBuzzer(beepLevel_g, 100, 50);
                    playBuzzer(beepLevel_g, 100, 50);
                }





    
                // 等待ID板状态更新（超时500ms，可按需调整）
                if (!waitForIdBoardUpdate(prev_ver, 500))
                {
                    // 若未更新，主动触发一次采样转换，以尽量刷新
                    transformBoard();
                }

                // 校验移动是否已在ID板体现
                //判断是否移动成功，根据ID板的状态，起点从有变无，终点从无变有
                bool applied = verifyMoveApplied(destArr_before, destArr, startPos_alt, endPos_alt);


                if (print_key)
                {
                    Serial.print("Move applied: ");
                    Serial.println(applied ? "true" : "false");
                }
                // 返回信息
                // if (cmd.id.length() > 0)
                // {
                //     sendprint("free_move", "success", "", nullptr, cmd.id);
                // }
                // else
                // {
                //     sendprint("free_move", "success", "", nullptr, "noid");
                // }
                // 创建JSON对象 response，返回给app的JSON格式
                StaticJsonDocument<256> response;
                response["cmd_name"] = "free_move";
                response["succeed_flag"] = applied;
                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }

                // 序列化JSON并通过蓝牙发送
                String responseStr;
                //创建字符串，将创建的JSON变为字符串，蓝牙只能发字符串
                serializeJson(response, responseStr);

                // 如果设备已连接，则发送通知
                if (deviceConnected_g)
                {
                    //将String 改为 char*，将字符串转换为 C 风格字符串（const char*）
                    pCharacteristic->setValue(responseStr.c_str());
                    //发送通知​
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
            }
            else if (cmdName == "sog")
            {
                Serial.println("开始处理开局着法序列");

                // 分割棋步字符串
                String moveSequence = cmdMovelist;
                std::vector<String> moves;
                int start = 0;
                int end = moveSequence.indexOf(',');

                while (end != -1)
                {
                    moves.push_back(moveSequence.substring(start, end));
                    start = end + 1;
                    end = moveSequence.indexOf(',', start);
                }
                moves.push_back(moveSequence.substring(start)); // 添加最后一个着法

                for (String move : moves)
                {
                    Serial.println(move);
                    if (move.length() >= 4)
                    { // 有效着法格式检查
                        String startPos = move.substring(0, 2);
                        String endPos = move.substring(2, 4);
                        // 转换坐标参数为判断ID板变化而服务
                        int startPos_alt = processString_alt(startPos);
                        int endPos_alt = processString_alt(endPos);
                        // 备份移动前ID板数据
                        char destArr_before[10][10];
                        for (int i = 0; i < 10; ++i)
                        {
                            for (int j = 0; j < 10; ++j)
                            {
                                destArr_before[i][j] = destArr[i][j];
                            }
                        }
                        // 记录当前版本号
                        uint32_t prev_ver = id_board_version;

                        if (startPos.charAt(0) == 'h' && endPos.charAt(0) == '0')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从h列到0列换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 5);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }

                        else if (startPos.charAt(0) == 'a' && endPos.charAt(0) == '9')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从a列到9列换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 5);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(1) == '1' && endPos.charAt(1) == '9')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从1行到9行换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 5);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(1) == '8' && endPos.charAt(1) == '0')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从8行到0行换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 5);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(0) == '0' && endPos.charAt(1) == '1')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从0列行到1行换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 1);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(0) == '0' && endPos.charAt(1) == '8')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从0列到8行换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 1);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(1) == '9' && endPos.charAt(1) == '1')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从9行到1行换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 2);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(1) == '0' && endPos.charAt(1) == '8')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从0行到8行换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 4);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(1) == '9' && endPos.charAt(1) == '1')
                        {
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            Serial.println("从9行到1行换磁铁");
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 4);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(0) == '0' && endPos.charAt(0) == 'h')
                        {
                            Serial.println("从0列到h列换磁铁");
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 1);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else if (startPos.charAt(0) == '9' && endPos.charAt(0) == 'a')
                        {
                            Serial.println("从9列到a列换磁铁");
                            int magnetNum = getMagnetNumFromPosition_DS(startPos, endPos);
                            logMove("", startPos, endPos, magnetNum);
                            String transfer_point = lookupIdInGridRange();
                            auto_chess("", startPos, transfer_point, 3);
                            auto_chess("", transfer_point, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                        }
                        else
                        {
                            // int magnetNum = getMagnetNumFromPosition(startPos, endPos);
                            // logMove("", startPos, endPos, magnetNum);
                            // // 调用现有的自动移动函数
                            // auto_chess("", startPos, endPos, getMagnetNumFromPosition(startPos, endPos));
                            // playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            // playBuzzer(beepLevel_g, 100, 50); // 关闭蜂鸣器
                            //                                   // 添加移动延迟防止机械碰撞
                            //                                   // vTaskDelay(500 / portTICK_PERIOD_MS);
                            //                                   // 移动前打印当前稳定状态
                            // 获取使用的电磁铁号码
                            int magnetNum = getMagnetNumFromPosition(startPos, endPos);
                            // 执行移动
                            auto_chess("", startPos, endPos, magnetNum);
                            playBuzzer(beepLevel_g, 100, 50);
                            playBuzzer(beepLevel_g, 100, 50);
                        }

                        // 等待ID板状态更新（超时500ms，可按需调整）
                        if (!waitForIdBoardUpdate(prev_ver, 500))
                        {
                            // 若未更新，主动触发一次采样转换，以尽量刷新
                            transformBoard();
                        }

                        // 校验移动是否已在ID板体现
                        bool applied = verifyMoveApplied(destArr_before, destArr, startPos_alt, endPos_alt);
                        Serial.print("Move applied: ");
                        Serial.println(applied ? "true" : "false");
                    }
                }
                // 发送完成响应
                if (cmd.id.length() > 0)
                {
                    sendprint("sog", "success", "", nullptr, cmd.id);
                }
                else
                {
                    sendprint("sog", "success", "", nullptr, "noid");
                }
            }

            else if (cmdName == "pathmove")
            {
                if (print_key)
                {
                    Serial.printf("开始处理pathmove命令\n");
                }

                // 检查路径字符串是否为空
                if (cmd.path_move_list.length() == 0)
                {
                    Serial.println("路径为空，无法执行路径移动");
                    return;
                }

                if (print_key)
                {
                    Serial.printf("收到路径: %s\n", cmd.path_move_list.c_str());
                }

                // 创建坐标向量
                std::vector<std::pair<int, int>> path_coordinates;

                // 按逗号分割字符串
                int start = 0;
                int end = cmd.path_move_list.indexOf(',');

                while (end >= 0 || start < cmd.path_move_list.length())
                {
                    // 获取当前坐标对字符串
                    String coord_str;
                    if (end >= 0)
                    {
                        coord_str = cmd.path_move_list.substring(start, end);
                        start = end + 1;
                    }
                    else
                    {
                        coord_str = cmd.path_move_list.substring(start);
                        start = cmd.path_move_list.length();
                    }

                    // 去除可能的空白字符
                    coord_str.trim();

                    // 按横杠分割，提取x和y坐标
                    int dash_pos = coord_str.indexOf('-');
                    if (dash_pos > 0)
                    {
                        int x = coord_str.substring(0, dash_pos).toInt();
                        int y = coord_str.substring(dash_pos + 1).toInt();

                        // 添加到坐标向量
                        path_coordinates.push_back(std::make_pair(x, y));
                        if (print_key)
                        {
                            Serial.printf("添加坐标: (%d, %d)\n", x, y);
                        }
                    }
                    else
                    {
                        if (print_key)
                        {
                            Serial.printf("无效的坐标格式: %s\n", coord_str.c_str());
                        }
                    }

                    // 更新end以查找下一个逗号
                    if (start < cmd.path_move_list.length())
                    {
                        end = cmd.path_move_list.indexOf(',', start);
                    }
                    else
                    {
                        end = -1;
                    }
                }

                // 输出解析后的坐标数量
                if (print_key)
                {
                    Serial.printf("成功解析 %d 个坐标点\n", path_coordinates.size());
                }

                // 如果成功解析了坐标，调用移动函数
                if (!path_coordinates.empty())
                {
                    // 默认使用电磁铁5
                    int magnetNum = 5;
                    path_auto_chess(path_coordinates, magnetNum);

                    // 发送成功响应
                    StaticJsonDocument<256> response;
                    response["cmd_name"] = "pathmove";
                    response["status"] = "success";

                    // 如果收到了id字段，将其原样返回
                    if (cmd.id.length() > 0)
                    {
                        response["id"] = cmd.id;
                    }

                    // 序列化JSON并通过蓝牙发送
                    String responseStr;
                    serializeJson(response, responseStr);
                    if (deviceConnected_g)
                    {
                        pCharacteristic->setValue(responseStr.c_str());
                        pCharacteristic->notify();
                    }
                    if (print_key)
                    {
                        Serial.println(responseStr);
                    }
                }
                else
                {
                    Serial.println("未能解析有效的路径坐标，无法执行移动");
                }
            }
            else if (cmdName == "get_equipment_data")
            {
                if (print_key)
                {
                    Serial.printf("开始处理get_equipment_data命令\n");
                }
                StaticJsonDocument<256> response;
                response["cmd_name"] = "get_equipment_data";
                response["battery_level"] = batteryLevel_g;
                response["motor_speed"] = get_current_speed();
                response["beep_volume"] = beepLevel_g;
                response["level"] = level_difficulty_g;
                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }
                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
            }
            else if (cmdName == "get_movement_position")
            {
                if (print_key)
                {
                    Serial.printf("开始处理get_movement_position命令\n");
                }
                Serial.print("current_col:");
                Serial.println(current_col);
                Serial.print("current_row:");
                Serial.println(current_row);

                // 创建JSON响应
                StaticJsonDocument<256> response;
                response["cmd_name"] = "get_movement_position";
                response["x"] = current_col;
                response["y"] = current_row;
                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }

                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);

                // 如果设备已连接，则发送通知
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
            }
            else if (cmdName == "get_id_board")
            {
                if (print_key)
                {
                    Serial.printf("开始处理get_id_board命令\n");
                }

                // 显示收到的ID值
                if (cmd.id.length() > 0)
                {
                    if (print_key)
                    {
                        Serial.printf("收到请求ID: %s\n", cmd.id.c_str());
                    }
                }

                // 将finalArr转换为10x10数组以便传递给send_print
                char id_arr[10][10];
                for (int i = 0; i < 10; i++)
                {
                    for (int j = 0; j < 10; j++)
                    {
                        id_arr[i][j] = finalArr[i][j];
                    }
                }

                // 创建JSON响应
                StaticJsonDocument<256> response;
                response["cmd_name"] = "get_id_board";

                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }

                // 将id_arr转换为字符串
                String boardStr = "";
                for (int i = 0; i < 10; i++)
                {
                    for (int j = 0; j < 10; j++)
                    {
                        boardStr += id_arr[i][j];
                    }
                }
                // response["id_board"] = boardStr;
                response["id_board_hex"] = idArrToHex(id_arr);

                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);

                // 如果设备已连接，则发送通知
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                    Serial.println("ID板状态已通过蓝牙发送");
                }
            }
            else if (cmdName == "clock_ticking")
            {
                if (print_key)
                {
                    Serial.printf("开始处理clock_ticking命令\n");
                }
                StaticJsonDocument<256> response;
                response["cmd_name"] = "clock_ticking";
                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }
                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
                // 让语音模块输出
                sendFrameToVoiceModule(0x05);
            }
            else if (cmdName == "illegal_move")
            {
                if (print_key)
                {
                    Serial.printf("开始处理illegal_move命令\n");
                }
                StaticJsonDocument<256> response;
                response["cmd_name"] = "illegal_move";
                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }
                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
                // 让语音模块输出
                sendFrameToVoiceModule(0x07);
            }
            else if (cmdName == "take_back")
            {
                StaticJsonDocument<256> response;
                response["cmd_name"] = "take_back";

                // 如果收到了id字段，将其原样返回
                if (cmd.id.length() > 0)
                {
                    response["id"] = cmd.id;
                    if (print_key)
                    {
                        Serial.printf("返回ID: %s\n", cmd.id.c_str());
                    }
                }
                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
            }
            else if (cmdName == "Start_game")
            {
                StaticJsonDocument<256> response;
                response["cmd_name"] = "Start_game";

                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
            }
            else if (cmdName == "level_up")
            {
                StaticJsonDocument<256> response;
                response["cmd_name"] = "level_up";

                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
            }
            else if (cmdName == "level_down")
            {
                StaticJsonDocument<256> response;
                response["cmd_name"] = "level_down";

                // 序列化JSON并通过蓝牙发送
                String responseStr;
                serializeJson(response, responseStr);
                if (deviceConnected_g)
                {
                    pCharacteristic->setValue(responseStr.c_str());
                    pCharacteristic->notify();
                }
                if (print_key)
                {
                    Serial.println(responseStr);
                }
            }
        }
        delay(500);
        batteryLevel_g = readBatteryStatus();
        // Serial.println("batteryLevel_g:" + String(batteryLevel_g));
    }
}

void send_hall_sensitivity(float sensitivity_value)
{
    // 创建一个命令包，使用0x02作为命令头标识灵敏度设置命令
    uint8_t cmd[6];
    cmd[0] = 0x02; // 命令头：灵敏度设置

    // 将浮点数转换为字节
    uint8_t *sensitivity_bytes = (uint8_t *)&sensitivity_value;
    cmd[1] = sensitivity_bytes[0];
    cmd[2] = sensitivity_bytes[1];
    cmd[3] = sensitivity_bytes[2];
    cmd[4] = sensitivity_bytes[3];
    cmd[5] = 0x03; // 命令尾

    // 发送命令到STM32
    mySerial.write(cmd, 6);

    if (print_key)
    {
        Serial.print("发送霍尔灵敏度值到STM32: ");
        Serial.println(sensitivity_value, 3);
    }
}

/**
 * @brief id板初始化
 * @note 功能特性：
 * - 向STM32发送握手信号，确保通信正常。
 */
void id_board_init()
{
    mySerial.begin(921600, SERIAL_8N1, 45, 38);

    // 发送握手信号
    mySerial.write(0x01);
    unsigned long startTime = millis();

    // 等待响应
    while (millis() - startTime < 2000)
    { // 1秒超时
        if (mySerial.available())
        {
            byte response = mySerial.read();
            if (response == 0x01)
            {
                id_status = true;
                if (print_key)
                {
                    Serial.println("ID板握手成功");
                }
                break;
            }
        }
    }
    // 创建命令队列，队列长度为10
    commandQueue = xQueueCreate(30, sizeof(Command)); // 增加队列大小
    if (commandQueue == NULL)
    {
        if (print_key)
        {
            Serial.println("创建命令队列失败!");
        }
        while (true)
        { /* 错误循环 */
        }
    }

    // 接收id板数据
    //  创建扫描任务并绑定到 Core 1
    xTaskCreatePinnedToCore(
        scannerTask,   // 任务函数
        "ScannerTask", // 任务名称
        4096,          // 堆栈大小
        NULL,          // 任务参数
        1,             // 任务优先级
        NULL,          // 任务句柄
        0              // 绑定到 Core 1
    );

    xTaskCreatePinnedToCore(
        commandProcessorTask,
        "PrinterComTask",
        8192,
        NULL,
        1,
        NULL,
        1);

    // xTaskCreatePinnedToCore(
    //     Task,
    //     "Task",
    //     8192,
    //     NULL,
    //     1,
    //     NULL,
    //     1);
}
